// 源于Inferno utils/6l/l.h及相关文件。
// https:
// 
// 版权所有©1994-1999朗讯科技有限公司。保留所有权利。
// 版权所有©1995-1997 C H Forsyth(forsyth@terzarima.net)
// 部分版权©1997-1999维塔诺瓦有限公司
// 部分版权©2000-2007维塔诺瓦控股有限公司（www.vitanuova.com）
// 部分版权©20042006布鲁斯·埃利斯
// 修订版权©2000-2007朗讯科技有限公司及其他
// 部分版权©2009 Go作者。保留所有权利。
// 
// 特此免费授予获得本软件及相关文档文件（以下简称“软件”）副本
// 的任何人在本软件中不受限制地处理
// 的权利，包括但不限于
// 使用、复制、修改、合并、发布、分发、再许可的权利，和/或出售本软件的
// 副本，并允许提供本软件的人员在以下条件下这样做：
// 
// 上述版权声明和本许可声明应包含在
// 本软件的所有副本或大部分中。
// 
// 本软件按“原样”提供，不提供任何形式的明示或暗示担保，包括但不限于适销性担保、
// 适用于特定用途和非侵权性担保。在任何情况下，
// 作者或版权持有人均不对任何索赔、损害赔偿或其他
// 责任承担责任，无论是合同诉讼、侵权诉讼还是其他诉讼，均由
// 因本软件或本软件的使用或其他交易引起，或与本软件或本软件的使用或其他交易有关。

package ld

import (
	"bufio"
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"cmd/link/internal/loader"
	"cmd/link/internal/sym"
	"debug/elf"
	"fmt"
)

type Shlib struct {
	Path string
	Hash []byte
	Deps []string
	File *elf.File
}

// Link保存从编译器写入目标代码或将输入读取到链接器的上下文。
type Link struct {
	Target
	ErrorReporter
	ArchSyms

	outSem chan int // 限制输出写入程序的数量
	Out    *OutBuf

	version int // 静态/文件本地符号的当前版本号

	Debugvlog int
	Bso       *bufio.Writer

	Loaded bool // 将所有输入加载为符号后设置

	compressDWARF bool

	Libdir       []string
	Library      []*sym.Library
	LibraryByPkg map[string]*sym.Library
	Shlibs       []Shlib
	Textp        []loader.Sym
	Moduledata   loader.Sym

	PackageFile  map[string]string
	PackageShlib map[string]string

	tramps []loader.Sym // 蹦床

	compUnits []*sym.CompilationUnit // 矮化编译单元
	runtimeCU *sym.CompilationUnit   // 最后一个运行时CUs。

	loader  *loader.Loader
	cgodata []cgodata // 要加载的cgo指令，三个字符串是loadcgo的参数

	datap  []loader.Sym
	dynexp []loader.Sym

	// Elf symtab变量。
	numelfsym int // 从0开始，1保留

	// 这些是链接器创建和编写的符号。
	// 您可以创建一个符号，在输出mmap中创建符号后，只需调用一个生成函数，而不是创建一个符号，并将其所有数据写入堆中。
	generatorSyms map[loader.Sym]generatorFunc
}

type cgodata struct {
	file       string
	pkg        string
	directives [][]string
}

// 从硬件堆栈指针到堆栈上本地
// 变量的最小可能偏移量。使用链接寄存器的架构将其值
// 保存在函数序言中的堆栈上，因此始终在
// 硬件堆栈指针和局部变量区域之间有一个指针。
func (ctxt *Link) FixedFrameSize() int64 {
	switch ctxt.Arch.Family {
	case sys.AMD64, sys.I386:
		return 0
	case sys.PPC64:
		// ppc64le上的PIC代码需要32字节的堆栈，而
		// 只需在ppc64x上始终使用这么多的堆栈就可以了。
		return int64(4 * ctxt.Arch.PtrSize)
	default:
		return int64(ctxt.Arch.PtrSize)
	}
}

func (ctxt *Link) Logf(format string, args ...interface{}) {
	fmt.Fprintf(ctxt.Bso, format, args...)
	ctxt.Bso.Flush()
}

func addImports(ctxt *Link, l *sym.Library, pn string) {
	pkg := objabi.PathToPrefix(l.Pkg)
	for _, imp := range l.Autolib {
		lib := addlib(ctxt, pkg, pn, imp.Pkg, imp.Fingerprint)
		if lib != nil {
			l.Imports = append(l.Imports, lib)
		}
	}
	l.Autolib = nil
}

// 分配一个新版本（即符号名称空间）。
func (ctxt *Link) IncVersion() int {
	ctxt.version++
	return ctxt.version - 1
}

// 返回最大版本号
func (ctxt *Link) MaxVersion() int {
	return ctxt.version
}

// generatorFunc是一种方便的类型。
// 一些链接器创建的符号很大，不应该真正存在于堆中。
// 这样的符号可以定义一个生成器函数。它们的字节可以直接在输出mmap中生成。
// 
// 在发出生成器符号内容之前应用重新定位。
// 需要重新定位的生成器符号可以两次写入。
// 在符号创建时的第一次传递只添加重新定位。
// 在内容生成时，第二个过程将添加其余部分。
// 有关示例，请参见generateFunctab。
// 
// 生成器函数不应增大符号大小。
// 生成器函数必须能够安全地同时使用。
// 
// 调用
// 生成器时，生成器符号将其数据设置到mmapped区域。
type generatorFunc func(*Link, loader.Sym)

// createGeneratorSymbol是创建生成器的一种方便方法。
func (ctxt *Link) createGeneratorSymbol(name string, version int, t sym.SymKind, size int64, gen generatorFunc) loader.Sym {
	ldr := ctxt.loader
	s := ldr.LookupOrCreateSym(name, version)
	ldr.SetIsGeneratedSym(s, true)
	sb := ldr.MakeSymbolUpdater(s)
	sb.SetType(t)
	sb.SetSize(size)
	ctxt.generatorSyms[s] = gen
	return s
}
